local U = require "packages/utility/utility"
local EV = {}

--令一名角色明置牌
---@param room Room @ 房间
---@param target ServerPlayer @ 明置牌的角色
---@param cards integer|integer[] @ 明置的牌
---@param skillName? string @ 此次明置的技能名
---@param proposer? ServerPlayer @ 操作者
EV.OpenCards = function (room, target, cards, skillName, proposer)
  skillName = skillName or ""
  cards = type(cards) == "table" and cards or {cards}
  local proposerId = proposer and proposer.id or target.id
  local shown = U.getMark(target, "@$shown")
  target:showCards(cards)
  for _, cardId in ipairs(cards) do
    local card = Fk:getCardById(cardId)
    room:setCardMark(card, "@@shown-inhand", 1)
    table.insertIfNeed(shown, cardId)
  end
  room:setPlayerMark(target, "@$shown", shown)
  room.logic:trigger("fk.CardOpened", target, {ids = cards, from = proposerId})
end

--令一名角色暗置牌
---@param room Room @ 房间
---@param target ServerPlayer @ 明置牌的角色
---@param cards integer|integer[] @ 明置的牌
---@param skillName? string @ 此次暗置的技能名
---@param proposer? ServerPlayer @ 操作者
EV.ConcealCards = function (room, target, cards, skillName, proposer)
  skillName = skillName or ""
  cards = type(cards) == "table" and cards or {cards}
  local proposerId = proposer and proposer.id or target.id
  local shown = U.getMark(target, "@$shown")
  for _, cardId in ipairs(cards) do
    local card = Fk:getCardById(cardId)
    room:setCardMark(card, "@@shown-inhand", 0)
    table.removeOne(shown, cardId)
  end
  if #shown == 0 then
    room:setPlayerMark(target, "@$shown", 0)
  else
    room:setPlayerMark(target, "@$shown", shown)
  end
  room.logic:trigger("fk.CardConcealed", target, {ids = cards, from = proposerId})
end

local shown_cards_clear = fk.CreateTriggerSkill{
  name = "shown_cards_clear",
  global = true,
  mute = true,
  refresh_events = {fk.AfterCardsMove},
  can_refresh = function(self, event, target, player, data)
    for _, move in ipairs(data) do
      if move.from == player.id and #U.getMark(player, "@$shown") ~= 0 then
        for _, info in ipairs(move.moveInfo) do
          return info.fromArea == Card.PlayerHand
        end
      end
    end
  end,
  on_refresh = function(self, event, target, player, data)
    local room = player.room
    for _, move in ipairs(data) do
      if move.from == player.id then--改为刷新机制
        local shown = table.filter(player:getCardIds(Player.Hand), function(id) return Fk:getCardById(id):getMark("@@shown-inhand") ~= 0 end)
        if #shown == 0 then
          room:setPlayerMark(player, "@$shown", 0)
        else
          room:setPlayerMark(player, "@$shown", shown)
        end
      end
    end
  end,
}

Fk:addSkill(shown_cards_clear)

Fk:loadTranslationTable{
  ["OpenCards"] = "明置手牌",
  ["ConcealCards"] = "暗置手牌",
  ["@$shown"] = "明置牌",
  ["@@shown-inhand"] = "明置",
  ["judgezone"] = "判定区",

  ["top"] = "牌堆顶",
  ["bottom"] = "牌堆底",
}

--A选择B区域内的一张牌
---@param room Room @ 房间
---@param chooser ServerPlayer @ 要被询问的人
---@param target ServerPlayer @ 被选牌的人
---@param flag any @ 用"hejs"字母组合表示能选择哪些区域, h 手牌区, e - 装备区, j - 判定区, s - 明置手牌
---@param reason string @ 原因，一般是技能名
---@param prompt? string @ 提示信息
---@return integer @ 选择的卡牌id
EV.askForCardChosen = function(room, chooser, target, flag, reason, prompt)
  prompt = prompt or ""
  local hcands = target:getCardIds(Player.Hand)
  local shown = table.filter(hcands, function(id) return Fk:getCardById(id):getMark("@@shown-inhand") ~= 0 end)
  local unshown = table.filter(hcands, function(id) return Fk:getCardById(id):getMark("@@shown-inhand") == 0 end)
  if #unshown > 0 and not chooser:isBuddy(target) then
    hcands = table.map(unshown, function() return -1 end)
  end
  local to_choose = {}
  if #unshown ~= 0 and string.find(flag, "h") then table.insertIfNeed(to_choose, {"$Hand", hcands}) end
  if #target:getCardIds(Player.Equip) ~= 0 and string.find(flag, "e") then table.insertIfNeed(to_choose, {"$Equip", target:getCardIds(Player.Equip)}) end
  if #target:getCardIds(Player.Judge) ~= 0 and string.find(flag, "j") then table.insertIfNeed(to_choose, {"judgezone", target:getCardIds(Player.Judge)}) end
  if #shown > 0 and string.find(flag, "s") and not chooser:isBuddy(target) then table.insertIfNeed(to_choose, {"OpenCards", shown}) end
  if #to_choose == 0 then return -1 end
  --local id = room:askForCardChosen(chooser, target, {card_data = to_choose}, reason, prompt)
  local command = "AskForCardChosen"
  prompt = prompt or ""
  room:notifyMoveFocus(chooser, command)
  local data = {target.id, {card_data = to_choose}, reason, prompt}
  local result = room:doRequest(chooser, command, json.encode(data))
  if result == "" then
    local handcards = {}
      for _, t in ipairs(to_choose) do
        table.insertTable(handcards, t[2])
      end
    if #handcards == 0 then return -1 end
    result = handcards[math.random(1, #handcards)]
  else
    result = tonumber(result)
  end
  if result == -1 then
    return table.random(unshown, 1)[1]
  else
    return result
  end
end

--A选择B区域内的多张牌

EV.askForCardsChosen = function(room, chooser, target, min, max, flag, reason, prompt)
  prompt = prompt or ""
  if min == 1 and max == 1 then
    return { EV.askForCardChosen(room, chooser, target, flag, reason, prompt) }
  end
  local hcands = target:getCardIds(Player.Hand)
  local shown = table.filter(hcands, function(id) return Fk:getCardById(id):getMark("@@shown-inhand") ~= 0 end)
  local unshown = table.filter(hcands, function(id) return Fk:getCardById(id):getMark("@@shown-inhand") == 0 end)
  if #unshown > 0 and not chooser:isBuddy(target) then
    hcands = table.map(unshown, function() return -1 end)
  end
  local to_choose = {}
  if #unshown ~= 0 and string.find(flag, "h") then table.insertIfNeed(to_choose, {"$Hand", hcands}) end
  if #target:getCardIds(Player.Equip) ~= 0 and string.find(flag, "e") then table.insertIfNeed(to_choose, {"$Equip", target:getCardIds(Player.Equip)}) end
  if #target:getCardIds(Player.Judge) ~= 0 and string.find(flag, "j") then table.insertIfNeed(to_choose, {"judgezone", target:getCardIds(Player.Judge)}) end
  if #shown > 0 and string.find(flag, "s") and not chooser:isBuddy(target) then table.insertIfNeed(to_choose, {"OpenCards", shown}) end
  if #to_choose == 0 then return {} end

  local cards = {}
  for _, t in ipairs(to_choose) do
    table.insertTable(cards, t[2])
  end
  --if #cards <= min then return cards end
  local data = {
    to = target.id,
    min = min,
    max = max,
    skillName = reason,
    prompt = prompt,
  }
  local cards_data = {}
  for _, t in ipairs(to_choose) do
    table.insert(cards_data, t)
  end
  local new_ret = {}
  local ret = room:askForPoxi(chooser, "AskForCardsChosen", cards_data, data, false)
  new_ret = table.filter(ret, function(id) return id ~= -1 end)
  local hidden_num = #ret - #new_ret
  if hidden_num > 0 then
    table.insertTable(new_ret,
    table.random(unshown, hidden_num))
  end
  return new_ret
end
 
EV.isFriend = function(player, target)
  if player.room and (not player.role_shown or not target.role_shown) then return false end
  if player.role == "" or target.role == "" then return false end
  return target.role == player.role or (table.contains({"lord", "loyalist"}, target.role) and table.contains({"lord", "loyalist"}, player.role))
end

EV.isEnemy = function(player, target)
  if player.room and (not player.role_shown or not target.role_shown) then return false end
  if player.role == "" or target.role == "" then return false end
  return player.role ~= target.role and not (player.role == "lord" and target.role == "loyalist")
  and not (player.role == "loyalist" and target.role == "lord")
end

EV.isSingleTarget = function(card)
  return card.trueName ~= "ghost_assault" and card.trueName ~= "meteor_falling" and card.trueName ~= "god_salvation"
  and card.trueName ~= "banquet" and card.trueName ~= "thorn_circle" and card.trueName ~= "doctrinal_proclama"
  and card.trueName ~= "snow_storm" and card.trueName ~= "armistices"
end

--选择场上的一张牌
EV.chooseCardonBoard = function(room, player, reason, prompt)
  if #table.filter(room.alive_players, function(p) return #p:getCardIds("ej") > 0 end) == 0 then return -1 end
  local _, ret = room:askForUseActiveSkill(player, "choose_players_skill", prompt, true, {
    targets = table.map(table.filter(room.alive_players, function(p)
      return #p:getCardIds("ej") > 0 end), Util.IdMapper),
    num = 1,
    min_num = 0,
    pattern = "",
    skillName = reason
  })
  if ret then
    if #ret.targets == 0 then return -1 end
    room:doIndicate(player.id, ret.targets)
    local id = room:askForCardChosen(player, room:getPlayerById(ret.targets[1]), "ej", reason)
    return id
  end
  return -1
end
--调整手牌至n张
EV.adjustHandCards = function(room, player, n, reason)
  if player:getHandcardNum() == n then return end
  if player:getHandcardNum() - n > 0 then
    room:askForDiscard(player, player:getHandcardNum() - n, player:getHandcardNum() - n, false, reason, false)
  elseif n - player:getHandcardNum() > 0 then
    player:drawCards(n - player:getHandcardNum(), reason)
  end
end
--含有的花色数
EV.countSuits = function(cards)
  local suits = {}
  for _, id in ipairs(cards) do
    if Fk:getCardById(id).suit ~= Card.NoSuit then
      table.insertIfNeed(suits, Fk:getCardById(id).suit)
    end
  end
  return #suits
end
--无效牌并播报
EV.nullifiedCard = function(data, card, target)
  if data.card.sub_type == Card.SubtypeDelayedTrick or data.card.type == Card.TypeEquip then
    AimGroup:cancelTarget(data, target.id)
  else
    table.insertIfNeed(data.nullifiedTargets, target.id)
  end
  target.room:sendLog{
    type = "#nullifiedCard",
    from = target.id,
    arg = data.card:toLogString(),
    toast = true,
  }
end

EV.choicelog = function(target, choice)
  target.room:sendLog{
    type = "#ChoiceLogtoast",
    from = target.id,
    arg = choice,
    toast = true,
  }
end

Fk:loadTranslationTable{
  ["#nullifiedCard"] = "%arg 对 %from 无效",
  ["#ChoiceLogtoast"] = "%from 选择了 %arg",
}

EV.changeHero = function(player, new_general, duputy, clearskills, maxHpChange)
  local room = player.room
  duputy = (duputy == nil) and false or duputy
  clearskills = (clearskills == nil) and false or clearskills
  maxHpChange = (maxHpChange == nil) and true or maxHpChange
  if clearskills then
    local skills = {}
    local other_skills = {}
    for _, s in ipairs(player.player_skills) do
      if s:isPlayerSkill(player) then
        table.insertIfNeed(skills, s.name)
      else
        table.insertIfNeed(other_skills, s.name)
      end
    end
    if #skills > 0 then
      room:handleAddLoseSkills(player, "-"..table.concat(skills, "|-"), nil, true, false)
    end
  end
  local generals = {player.general}
  if player.deputyGeneral ~= "" then
    table.insert(generals, player.deputyGeneral)
  end
  room:returnToGeneralPile(generals)
  room:findGeneral(new_general)
  room:changeHero(player, new_general, false, duputy, true, false)
  if player.dead or not maxHpChange then return false end
  local x = player:getGeneralMaxHp()
  if player.role == "lord" and (#room.players > 4 or room.settings.gameMode == "ev_1v2_mode") then
    x = x + 1
  end
  if player.maxHp ~= x then
    room:changeMaxHp(player, x - player.maxHp)
  end
end

--阵法技

--- 获取与角色成队列的其余角色
---@param player Player
---@return ServerPlayer[]? @ 队列中的角色
EV.getFormationRelation = function(player)
  --if player:isRemoved() then return {} end
  local players = {}
  local p = player
  while true do
    p = p:getNextAlive(false)
    if p == player then break end
    if EV.isFriend(p, player) then
      table.insert(players, p)
    else
      break
    end
  end
  p = player
  while true do
    p = p:getLastAlive(false)
    if p == player then break end
    if EV.isFriend(p, player) then
      table.insertIfNeed(players, p)
    else
      break
    end
  end
  return players
end

--- 确认与某角色是否处于队列中
---@param player Player @ 角色1
---@param target Player @ 角色2，若为 player 即 player 是否处于某一队列
---@return bool
EV.inFormationRelation = function(player, target)
  if target == player then
    return #EV.getFormationRelation(player) > 0
  else
    return table.contains(EV.getFormationRelation(player), target)
  end
end

--- 确认与某角色是否处于围攻关系
---@param player ServerPlayer @ 围攻角色1
---@param target ServerPlayer @ 围攻角色2，可填 player
---@param victim ServerPlayer @ 被围攻角色
---@return bool
EV.inSiegeRelation = function(player, target, victim)
  if not EV.isEnemy(player, victim) or not EV.isFriend(player, target) then return false end
  if player == target then
    return (player:getNextAlive() == victim and player:getNextAlive(false, 2) ~= player and EV.isFriend(player:getNextAlive(false, 2), player))
    or (victim:getNextAlive() == player and player:getLastAlive(false, 2) ~= player and EV.isFriend(player:getLastAlive(false, 2), player))
  else
    return (player:getNextAlive() == victim and victim:getNextAlive() == target) -- P V T
    or (victim:getNextAlive() == player and target:getNextAlive() == victim) -- T V P
  end
end

--获取短路径之间的角色
EV.getShortRoute = function(player, target)
  local nexts = {}
  local lasts = {}
  local p = player
  while true do
    p = p:getNextAlive(false)
    table.insert(nexts, p)
    if p == target then break end
  end
  p = player
  while true do
    p = p:getLastAlive(false)
    table.insert(lasts, p)
    if p == target then break end
  end
  if #nexts <= #lasts then
    return nexts
  else
    return lasts
  end
end

--中央区（伪）

EV.center_area = fk.CreateTriggerSkill{
  name = "#ev__center_area",
  --priority = -1,
  --mute = true,
  refresh_events = {fk.AfterCardsMove, fk.AfterTurnEnd},
	can_refresh = function(self, event, target, player, data)
    if player.room.settings.gameMode == "ls_2v2" or player.room.settings.gameMode == "ls_1v2_mode" then return false end
		return player:hasSkill(self.name, true, true) and player.room:getTag("RoundCount")
	end,
  on_refresh = function(self, event, target, player, data)
    local room = player.room
    if event == fk.AfterCardsMove then
      local ids = {}
      local logic = room.logic
      local events = logic.event_recorder[GameEvent.MoveCards] or Util.DummyTable
      if not logic:getCurrentEvent():findParent(GameEvent.Turn, true) then return {} end
      local end_id = logic:getCurrentEvent():findParent(GameEvent.Turn, true).id
      for i = #events, 1, -1 do
        local e = events[i]
        if e.id < end_id then break end
        for _, move in ipairs(e.data) do
          if move.toArea == Card.DiscardPile then
            for _, info in ipairs(move.moveInfo) do
              table.insertIfNeed(ids, info.cardId)
            end
          end
        end
      end
      local cards = table.simpleClone(ids)
      for _, id in ipairs(cards) do
        if room:getCardArea(id) ~= Card.DiscardPile then
          table.removeOne(ids, id)
        end
      end
      room:setBanner("@$centerarea-turn", ids)
      room.logic:trigger("fk.CenterareaChanged", player, {ids = ids})
    elseif event == fk.AfterTurnEnd then
      room:setBanner("@$centerarea-turn", {})
      room.logic:trigger("fk.CenterareaChanged", player, {ids = {}})
    end
  end,
}

EV.getCenterArea = function (room)
  local ids = {}
  local logic = room.logic
  local events = logic.event_recorder[GameEvent.MoveCards] or Util.DummyTable
  if not logic:getCurrentEvent():findParent(GameEvent.Turn, true) then return {} end
  local end_id = logic:getCurrentEvent():findParent(GameEvent.Turn, true).id
  for i = #events, 1, -1 do
    local e = events[i]
    if e.id < end_id then break end
    for _, move in ipairs(e.data) do
      if move.toArea == Card.DiscardPile then
        for _, info in ipairs(move.moveInfo) do
          table.insertIfNeed(ids, info.cardId)
        end
      end
    end
  end
  local cards = table.simpleClone(ids)
  for _, id in ipairs(cards) do
    if room:getCardArea(id) ~= Card.DiscardPile then
      table.removeOne(ids, id)
    end
  end
  return ids
end
--护盾（氯）
local ev_shield_skill = fk.CreateTriggerSkill{
  name = "#ev_shield_skill",
  global = true,
  mute = true,
  events = {fk.DamageInflicted, fk.EventPhaseStart},
  can_trigger = function(self, event, target, player, data)
    if player.room.settings.gameMode == "ls_2v2" or player.room.settings.gameMode == "ls_1v2_mode" then return false end
    if event == fk.EventPhaseStart then
      return target == player and target.phase == Player.Start and #target:getPile("shield") > 0
    else
		  return target == player and #target:getPile("shield") > 0
    end
  end,
  on_cost = Util.TrueFunc,
  on_use = function(self, event, target, player, data)
    local room = player.room
    if event == fk.DamageInflicted then
      if #player:getPile("shield") > data.damage then
        local n = data.damage
        local card = room:askForCard(player, n, n, false, self.name, false, ".|.|.|shield|.|.", "#shield-invoke:::"..n, "shield")
        if #card == 0 then card = table.random(player:getPile("shield"), n) end
        room:moveCardTo(card, Card.DiscardPile, nil, fk.ReasonPutIntoDiscardPile, "", "", true)
        room.logic:trigger("fk.ShieldCosted", player, {ids = card, from = data.from or nil})
        return true
      else
        data.damage = data.damage - #player:getPile("shield")
        local ids = player:getPile("shield")
        room:moveCardTo(ids, Card.DiscardPile, nil, fk.ReasonPutIntoDiscardPile, "", "", true)
        room.logic:trigger("fk.ShieldCosted", player, {ids = ids, from = data.from or nil})
      end
    else
      room:moveCardTo(player:getPile("shield"), Card.PlayerHand, player, fk.ReasonJustMove, self.name, "", true)
    end
  end
}

Fk:addSkill(ev_shield_skill)

Fk:loadTranslationTable{
["#ev__center_area"] = "中央区",
["@$centerarea-turn"] = "本回合弃牌堆",

["shield"] = "护盾",
["@@shield"] = "护盾",
["#ev_shield_skill"] = "护盾",
["#shield-invoke"] = "护盾：请弃置 %arg 张护盾抵消 %arg 点伤害",
["#shield_remove"] = "%from 移除了护盾：%arg",
}

return EV